import java.io.*;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TreeMap;

class NetReq {
    private Map<String,String> myHeader;
    public NetReq(Map<String,String> Header){
        this.myHeader = Header;
    }
    public NetReq(File Header){
        myHeader = readHeader(Header);
    }
    private Map<String,String> readHeader(File Header){
        Map<String,String> myHeader = new TreeMap<>();
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(Header), StandardCharsets.UTF_8));
            String str;
            String delim = ": ";
            while ((str = br.readLine()) != null) {
                myHeader.put(str.substring(0, str.indexOf(delim)),
                        //去掉gzip，因为我这边懒得考虑解压缩的问题
                        str.substring(str.indexOf(delim) + delim.length()).replaceAll("gzip", ""));
            }
            br.close();
        }catch (Exception e){
            e.printStackTrace();
        }
        return myHeader;
    }

    public String get(String src_url,File Header) throws Exception{
        myHeader = readHeader(Header);
        return get(src_url);
    }

    public String get(String src_url) throws IOException {
        URL url = null;
        //1, 取得URL对象
        try {
            //System.out.println(path);
            url = new URL(src_url);
        }catch (Exception e) {
            System.err.println(src_url);
            e.printStackTrace();
            return null;
        }
        //2, 建立连接,并不断设置请求头
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        if(myHeader!=null) {
            for (String key : myHeader.keySet()) {
                conn.addRequestProperty(key, myHeader.get(key));
            }
        }
        //3, 请求方式为GET
        conn.setRequestMethod("GET");
        //4, 允许输入
        conn.setDoInput(true);
        //5, 获取连接的输入流（输入主机返回的数据），并放在字符串中
        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
        String str = null;
        StringBuilder sb = new StringBuilder();
        while((str = br.readLine())!=null) {
            sb.append(str).append('\n');
        }
        return sb.toString();
    }

    public void download(String url,String File){
        try {
            download(new URL(url), File);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void download(URL url,String file) throws Exception{
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        long total_length = conn.getContentLength();
        conn.disconnect();
        conn = (HttpURLConnection)url.openConnection();
        File f = new File(file);
        f.getParentFile().mkdirs();
        if(f.exists()) {
            //文件存在，且被挖空或大小正确
            if(f.length()==0||f.length()==total_length) return;
        }
        //断点续传
        long position = f.exists() ? f.length() : 0;
        FileOutputStream fos = new FileOutputStream(file, true);
        FileChannel fileChannel = fos.getChannel();
        while(position<total_length) {
            try {
                conn.setReadTimeout(5_000);//超时时间每次都需要重新设置才有效
                conn.setRequestProperty("Range", String.format("bytes=%d-%d", position, total_length));
                ReadableByteChannel readableByteChannel = Channels.newChannel(conn.getInputStream());
                long count = 4 * 1024 * 1024;
                //Thread.sleep(1_000);
                while (position < total_length) {
                    long inc = fileChannel.transferFrom(readableByteChannel, position, count);
                    if(inc==0) throw new SocketTimeoutException();
                    position += inc;
                    System.out.println(String.format("\r文件名：%s, 下载进度：%.02f%%", file, position * 100.0 / total_length));
                }
            } catch (SocketTimeoutException ste) {
                //ste.printStackTrace();
                conn.disconnect();
                System.err.println("网络连接超时，尝试重连");
                Thread.sleep(1_000);
                conn = (HttpURLConnection)url.openConnection();
            }
        }
        if(conn!=null) conn.disconnect();
        fos.close();

    }

}
